(function ( exports ) {

    //region RequestBase

    function RequestBase ( requestId ) {
        this.requestId = requestId;
        this.parameters = {};
    }

    RequestBase.prototype = {
        requestNamespace:'',
        requestNamespaceHash:'',
        requestName:'',
        namespace:'http://schemas.microsoft.com/xrm/2011/Contracts',
        namespaceHash:COSA.Util.getHashString('http://schemas.microsoft.com/xrm/2011/Contracts'),
        serialize: function ( sc ) {
            var defLtr = sc.letter(this.namespaceHash),
                rqLtr, tmp;

            if ( typeof defLtr == 'undefined' ) {
                defLtr = sc.register(this.namespace);
                sc.pushParent(defLtr.ns(), this.namespace);
            }

            rqLtr = sc.letter(this.requestNamespaceHash);

            if ( typeof rqLtr == 'undefined' ) {
                rqLtr = sc.register(this.requestNamespace);
                sc.pushParent(rqLtr.ns(), this.requestNamespace);
            }

            sc.pushParent('i:type', rqLtr.tag(COSA.Util.formatString('{0}Request', this.requestName)));

            this.retrieveParameters();

            sc.snapshot();

            tmp = this.getTemplate(sc, defLtr);

            sc.restore();

            return tmp;
        },
        getTemplate: function ( sc, ltr ) {
            var parametersNode = {
                    tag: ltr.tag('Parameters'),
                    nodes: (new COSA.RequestParametersCollection(this.parameters)).serialize(sc)
                },
                requestIdNode = {
                    tag: ltr.tag('RequestId')
                },
                requestNameNode = {
                    tag: ltr.tag('RequestName'),
                    val: this.requestName
                };

            sc.appendToParent(parametersNode);

            if ( typeof this.requestId != 'undefined' && this.requestId !== null ) {
                requestIdNode.val = this.requestId;
            }
            else {
                requestIdNode.attr = { 'i:nil':'true' };
            }

            return [ parametersNode, requestIdNode, requestNameNode ];
        },
        execute: function ( callback ) {
            COSA.Core.Execute(this, callback);
        },
        deserialize: function ( node ) {
            return (this.getResponseInstance())
                .deserialize(node);
        },
        deserializeMultiple: function ( node ) {
            return (this.getResponseInstance())
                .deserializeMultiple(node);
        },
        getResponseInstance: function () {},
        retrieveParameters: function () {}
    };

    //endregion RequestBase

    //region XrmRequest

    /**
     * Base class for requests placed in 'http://schemas.microsoft.com/xrm/2011/Contracts' namespace
     * @param requestName Name of request
     * @param requestId Request unique identifier
     * @constructor
     */
    function XrmRequest ( requestId ) {
        RequestBase.call(this, requestId);
    }

    COSA.Util.deriveOverride(RequestBase, XrmRequest, {
        requestNamespace:'http://schemas.microsoft.com/xrm/2011/Contracts',
        requestNamespaceHash:COSA.Util.getHashString('http://schemas.microsoft.com/xrm/2011/Contracts')
    });

    //endregion XrmRequest

    //region CrmRequest

    /**
     * Base class for request placed in '' namespace
     * @param requestName Request name
     * @param requestId Request unique identifier
     * @constructor
     */
    function CrmRequest ( requestId ) {
        RequestBase.call(this, requestId);
    }

    COSA.Util.deriveOverride(RequestBase, CrmRequest, {
        requestNamespace:'http://schemas.microsoft.com/crm/2011/Contracts',
        requestNamespaceHash:COSA.Util.getHashString('http://schemas.microsoft.com/crm/2011/Contracts')
    });

    //endregion CrmRequest

    //region request

    function RequestWrapper ( request ) {
        this.rq = request;
    }

    RequestWrapper.prototype.serialize = function ( sc ) {
        var res = {
            tag: 'request',
            nodes: this.rq.serialize(sc)
        };

        sc.appendToParent(res);

        return res;
    };

    //region request

    //region OrganizationRequest

    function OrganizationRequestWrapper ( request ) {
        this.rq = request;
    }

    OrganizationRequestWrapper.prototype.namespace = 'http://schemas.microsoft.com/xrm/2012/Contracts';
    OrganizationRequestWrapper.prototype.namespaceHash = COSA.Util.getHashString('http://schemas.microsoft.com/xrm/2012/Contracts');
    OrganizationRequestWrapper.prototype.type = 'OrganizationRequest';

    OrganizationRequestWrapper.prototype.serialize = function ( sc ) {
        var ltr = sc.letter(this.namespaceHash),
            res = {
                tag: ltr.tag(this.type)
            };

        sc.snapshot();

        res.nodes = this.rq.serialize(sc);
        sc.appendToParent(res);

        sc.restore();

        return res;
    };

    //endregion OrganizationRequest

    //region CustomAction

    function CustomAction ( requestName, target, args, requestId ) {
        this.requestName = requestName;
        this.args = args;
        this.target = target;
        this.parameters = null;
        this.requestId = requestId;
    }



    CustomAction.prototype = {
        namespace: 'http://schemas.microsoft.com/xrm/2011/Contracts',
        namespaceHash: COSA.Util.getHashString('http://schemas.microsoft.com/xrm/2011/Contracts'),
        serialize: function ( sc ) {
            var defLtr = sc.letter(this.namespaceHash),
                rqLtr, tmp;

            if ( typeof defLtr == 'undefined' ) {
                defLtr = sc.register(this.namespace);
                sc.pushParent(defLtr.ns(), this.namespace);
            }

            this.retrieveParameters();

            sc.snapshot();

            tmp = this.getTemplate(sc, defLtr);

            sc.restore();

            return tmp;
        },
        getTemplate: function ( sc, ltr ) {
            var parametersNode = {
                    tag: ltr.tag('Parameters'),
                    nodes: (new COSA.RequestParametersCollection(this.parameters)).serialize(sc)
                },
                requestIdNode = {
                    tag: ltr.tag('RequestId')
                },
                requestNameNode = {
                    tag: ltr.tag('RequestName'),
                    val: this.requestName
                };

            sc.appendToParent(parametersNode);

            if ( typeof this.requestId != 'undefined' && this.requestId !== null ) {
                requestIdNode.val = this.requestId;
            }
            else {
                requestIdNode.attr = { 'i:nil':'true' };
            }

            return [ parametersNode, requestIdNode, requestNameNode ];
        },
        getResponseInstance: function () {
            return new COSA.Response.CustomResponse(this.requestName);
        },
        retrieveParameters: function () {
            this.parameters = this.args;
            this.parameters['Target'] = this.target;
        },
        execute: function ( callback ) {
            COSA.Core.Execute(this, callback);
        },
        deserialize: function ( node ) {
            return (this.getResponseInstance())
                .deserialize(node);
        },
        deserializeMultiple: function ( node ) {
            return (this.getResponseInstance())
                .deserializeMultiple(node);
        }
    };

    //endregion CustomAction

    exports.XrmRequest = XrmRequest;
    exports.CrmRequest = CrmRequest;
    exports.CustomAction = CustomAction;

    exports.RequestWrapper = RequestWrapper;
    exports.OrganizationRequestWrapper = OrganizationRequestWrapper;

})(COSA.Request);